import { onUnmounted, onDeactivated, getCurrentInstance, type ComponentInternalInstance } from 'vue'

interface EventBusOptions {
    debounce?: number      // 防抖时间（毫秒）
    throttle?: number      // 节流时间（毫秒）
    maxCalls?: number      // 最多触发次数
    debug?: boolean        // 调试模式：打印注册/触发/解绑日志
    buffer?: boolean         // 事件缓冲，先触发后监听也能接收
    middleware?: (payload: any) => any | false  // 中间件，返回 false 阻止事件继续
    offOnDeactivated?: boolean // 在组件非激活时自动解绑
}

export type Handler<T = any> = (payload: T, componentName?: string) => void

type EventName = string | Symbol

type ListenerRecord<T = any> = {
    original: Handler<T>
    wrapped: Handler<T>
    options: EventBusOptions
}

export class EventBus {
    private events = new Map<EventName, ListenerRecord<any>[]>()
    private bufferMap = new Map<EventName, any>()

    private wrapHandler<T = any>(event: EventName, handler: Handler<T>, options: EventBusOptions): Handler<T> {
        let count = 0
        let lastTime = 0
        let timer: any = null

        let wrapped: Handler<T> = (payload: T, componentName?: string) => {
            handler(payload)
            if (options.debug) console.log(`[EventBus] ${String(event)} [ComponentName] ${componentName}`, payload)
        }

        if (options.throttle) {
            const throttleTime = options.throttle
            wrapped = (payload: T, componentName?: string) => {
                const now = Date.now()
                if (now - lastTime >= throttleTime) {
                    lastTime = now
                    handler(payload)
                    if (options.debug) console.log(`[EventBus] [throttle] ${String(event)} [ComponentName] ${componentName}`, payload)
                }
            }
        } else if (options.debounce) {
            const debounceTime = options.debounce
            wrapped = (payload: T, componentName?: string) => {
                if (timer) clearTimeout(timer)
                timer = setTimeout(() => {
                    handler(payload)
                    if (options.debug) console.log(`[EventBus] [debounce] ${String(event)} [ComponentName] ${componentName}`, payload)
                }, debounceTime)
            }
        }

        if (options.maxCalls !== undefined) {
            const originalWrapped = wrapped
            wrapped = (payload: T, componentName?: string) => {
                if (count >= options.maxCalls!) return
                count++
                originalWrapped(payload)
                if (options.debug) console.log(`[EventBus] [maxCalls] ${String(event)} (${count}) [ComponentName] ${componentName}`, payload)
            }
        }

        return wrapped
    }

    on<T = any>(event: EventName, handler: Handler<T>, options: EventBusOptions = {}): () => void {
        const wrappedHandler = this.wrapHandler(event, handler, options)
        const record: ListenerRecord<T> = { original: handler, wrapped: wrappedHandler, options }

        if (!this.events.has(event)) this.events.set(event, [])
        this.events.get(event)!.push(record as ListenerRecord<any>)

        // 触发缓冲事件（如果有）
        if (options.buffer && this.bufferMap.has(event)) {
            wrappedHandler(this.bufferMap.get(event))
        }

        if (options.debug) {
            console.log(`[EventBus] on: ${String(event)}`, { options })
        }

        return () => this.off(event, handler)
    }

    once<T = any>(event: EventName, handler: Handler<T>, options: EventBusOptions = {}) {
        const onceWrapper: Handler<T> = (payload) => {
            handler(payload)
            this.off(event, handler)
        }
        this.on(event, onceWrapper, options)
    }

    emit<T = any>(event: EventName, payload: T, componentName?: string) {
        this.bufferMap.set(event, payload)
        const listeners = this.events.get(event)
        if (!listeners) return

        for (const { wrapped, options } of listeners) {
            let processedPayload = payload
            if (options.middleware) {
                const result = options.middleware(payload)
                if (result === false) {
                    if (options.debug) console.log(`[EventBus] middleware blocked event: ${String(event)}`)
                    continue
                }
                processedPayload = result
            }
            wrapped(processedPayload, componentName)
            if (options.debug) {
                console.log(`[EventBus] emit: ${String(event)}`, processedPayload)
            }
        }
    }

    off<T = any>(event: EventName, handler: Handler<T>) {
        const listeners = this.events.get(event)
        if (!listeners) return

        const filtered = listeners.filter(record => record.original !== handler)
        if (filtered.length === 0) {
            this.events.delete(event)
            this.bufferMap.delete(event)
        } else {
            this.events.set(event, filtered)
        }
    }

    clear(event?: EventName) {
        if (event) {
            this.events.delete(event)
            this.bufferMap.delete(event)
        } else {
            this.events.clear()
            this.bufferMap.clear()
        }
    }


}

export const globalEventBus = new EventBus()

export function useEventBus<T = any>(event: EventName, handler: Handler<T>, options?: EventBusOptions) {
    const off = globalEventBus.on(event, handler, options)
    onUnmounted(() => off())
    if (options?.offOnDeactivated) {
        onDeactivated(() => off())
    }
}

export function emitEvent<T = any>(event: EventName, payload: T, debugSign?: string | ComponentInternalInstance) {
    let componentName = ''
    if (typeof debugSign === 'string') {
        componentName = debugSign
    } else if (debugSign && debugSign.type && debugSign.type.__file) {
        componentName = debugSign.type.__file
    }
    globalEventBus.emit(event, payload, componentName)
}

export function useEmitWithContext<T = any>() {
    const instance = getCurrentInstance()
    if (!instance) {
        throw new Error('useEmitWithContext must be used in setup()')
    }

    return { emitEvent: (event: EventName, payload: T) => emitEvent(event, payload, instance) }
}

export function onceEvent<T = any>(event: EventName, handler: Handler<T>, options?: EventBusOptions) {
    globalEventBus.once(event, handler, options)
}

export function clearEvent(event?: EventName) {
    globalEventBus.clear(event)
}

const namespaceCache = new Map<string, ReturnType<typeof createSingleNamespaceBus>>()


function createSingleNamespaceBus(namespace: string) {
    const prefix = `${namespace}:`

    const wrapName = (event: EventName) =>
        typeof event === 'string' ? `${prefix}${event}` : event

    return {
        on<T = any>(event: EventName, handler: Handler<T>, options?: EventBusOptions) {
            return globalEventBus.on(wrapName(event), handler, options)
        },
        emit<T = any>(event: EventName, payload: T) {
            return globalEventBus.emit(wrapName(event), payload)
        },
        once<T = any>(event: EventName, handler: Handler<T>, options?: EventBusOptions) {
            return globalEventBus.once(wrapName(event), handler, options)
        },
        off<T = any>(event: EventName, handler: Handler<T>) {
            return globalEventBus.off(wrapName(event), handler)
        },
        clear(event?: EventName) {
            if (event) {
                return globalEventBus.clear(wrapName(event))
            } else {
                // 清空整个命名空间相关的事件
                for (const key of globalEventBus['events'].keys()) {
                    if (typeof key === 'string' && key.startsWith(prefix)) {
                        globalEventBus.clear(key)
                    }
                }
            }
        },
    }
}

/**
 * 创建命名空间事件总线，避免事件名称冲突
 * 
 * 命名空间缓存 重复创建相同 namespace 复用同一个对象
 */
export function createNamespacedBus(namespace: string) {
    if (namespaceCache.has(namespace)) {
        return namespaceCache.get(namespace)!
    }
    const nsBus = createSingleNamespaceBus(namespace)
    namespaceCache.set(namespace, nsBus)
    return nsBus
}

/** 清除某个 namespace 缓存 */
export function clearNamespace(namespace: string) {
    namespaceCache.delete(namespace)
}

/** 调试查看当前所有 namespace */
export function listNamespaces() {
    return Array.from(namespaceCache.keys())
}
